package my.edu.model.information.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import my.edu.common.constant.MessageConstant;
import my.edu.common.domain.PageDTO;
import my.edu.common.domain.PageVO;
import my.edu.common.exception.AccountTypeException;
import my.edu.common.exception.BillsException;
import my.edu.common.utils.PageUtils;
import my.edu.model.information.entity.Accounttype;
import my.edu.model.information.entity.dto.AccountSaveDTO;
import my.edu.model.information.entity.dto.AccountSearch;
import my.edu.model.information.mapper.AccountTypeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import my.edu.model.information.entity.Account;
import my.edu.model.information.mapper.AccountMapper;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements IAccountService {

    @Autowired
    private AccountTypeMapper accountTypeMapper;

    @Override
    public void savenew(AccountSaveDTO accountSaveDTO) {
        Account account = BeanUtil.copyProperties(accountSaveDTO, Account.class);
        account.setEstablishmentDate(LocalDate.now());//设置建账时间
        account.setBalance(accountSaveDTO.getInitialBalance());//设置余额
        account.setStatus(1);//默认启用
        checkAccountType(accountSaveDTO.getAccountType());
        save(account);
    }


    @Override
    public List<Accounttype> getAccountType() {
        return accountTypeMapper.selectList(null);
    }

    @Override
    public void updateByDTO(AccountSaveDTO accountSaveDTO) {
        checkAccountType(accountSaveDTO.getAccountType());
        Account account = BeanUtil.copyProperties(accountSaveDTO, Account.class);
        account.setBalance(accountSaveDTO.getInitialBalance());//设置余额
        updateById(account);
    }

    @Override
    @Transactional
    public void addBalance(Map<Long, BigDecimal> accountIdToTotalAmountMap) {
        accountIdToTotalAmountMap.forEach((accountId, totalAmount) -> {
            update(Wrappers.<Account>lambdaUpdate()
                    .setSql("balance = balance + " + totalAmount)
                    .eq(Account::getId, accountId));
        });
    }


    @Override
    @Transactional
    public void subBalance(Map<Long, BigDecimal> accountIdToTotalAmountMap) {
        accountIdToTotalAmountMap.forEach((accountId, totalAmount) -> {
            update(Wrappers.<Account>lambdaUpdate()
                    .setSql("balance = balance - " + totalAmount)
                    .eq(Account::getId, accountId));
        });
    }

    @Override
    public String getAccountNameById(Long settlementAccountId) {
        Account byId = getById(settlementAccountId);
        if (byId == null) throw new BillsException(MessageConstant.CAN_NOT_FOUND_ACCOUNT);
        if (byId.getStatus() == 0)throw new BillsException(MessageConstant.CAN_NOT_USE_ACCOUNT);
        return byId.getAccountName();
    }

    @Override
    public void setStatus(List<Long> longs, int status) {
        lambdaUpdate().set(Account::getStatus, status).in(Account::getId, longs).update();
    }

    @Override
    public PageVO<Account> list(PageDTO pageDTO, AccountSearch accountSearch) {
        MPJLambdaWrapper<Account> wrapper = new MPJLambdaWrapper<>(Account.class);
        wrapper.selectAll()
                .like(accountSearch.getAccountNo() != null, Account::getAccountNo, accountSearch.getAccountNo())
                .like(accountSearch.getAccountName() != null, Account::getAccountName, accountSearch.getAccountName())
                .like(accountSearch.getAccountType() != null, Account::getAccountType, accountSearch.getAccountType());
        Page<Account> page = baseMapper.selectJoinPage(new Page<>(pageDTO.getPage(), pageDTO.getSize()), Account.class, wrapper);
        return PageUtils.toPage(page);
    }

    @Override
    public JSONArray listOnly() {
        List<Account> supplierList = list();
        JSONArray res=new JSONArray();
        for (Account supplier : supplierList) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.putOnce("id",supplier.getId());
            jsonObject.putOnce("name",supplier.getAccountName());
            res.add(jsonObject);
        }
        return res;
    }

    @Override
    public void checkAccount(Long settlementAccountId) {
        Account byId = getById(settlementAccountId);
        if (byId == null) throw new BillsException(MessageConstant.CAN_NOT_FOUND_ACCOUNT);
        if (byId.getStatus() == 0)throw new BillsException(MessageConstant.CAN_NOT_USE_ACCOUNT);
    }

    @Override
    public JSONObject getListWithBalance(PageDTO pageDTO, String name, String serialNo) {
        Page<Account> page = lambdaQuery()
                .and(StringUtils.hasText(name), i -> i
                        .like(Account::getAccountName, name)
                        .or()
                        .like(Account::getAccountNo, name))
                .eq(Account::getStatus, 1)
                .page(new Page<>(pageDTO.getPage(), pageDTO.getSize()));
        List<Account> accountList = page.getRecords();
        BigDecimal allCurrentAmount = accountList.stream().map(Account::getBalance).reduce(BigDecimal.ZERO, BigDecimal::add);

        return new JSONObject()
                .putOnce("total", page.getTotal())
                .putOnce("data", page.getRecords())
                .putOnce("allCurrentAmount", allCurrentAmount);
    }

    private boolean checkAccountType(String accountType) {
        List<String> accountTypeList = getAccountType().stream().map(Accounttype::getType).toList();
        if (!accountTypeList.contains(accountType)) {
            throw new AccountTypeException(MessageConstant.ACCOUNTTYPE_NOT_FOUND);
        }
        return true;
    }

}
